import librosa
import soundfile as sf
import pygame
from gtts import gTTS
import tkinter as tk
import win32gui
import win32con
import threading
import hashlib
import os
import json
import re

def read_config():
    with open(file="config.json", mode="r", encoding="utf-8") as f:
        config = json.loads(f.read())
    with open(file="fixed_collocation.json", mode="r", encoding="utf-8") as f:
        fixed_collocation = json.loads(f.read())
    with open(file="word_replacement.json", mode="r", encoding="utf-8") as f:
        word_replacement = json.loads(f.read())
    return config, fixed_collocation, word_replacement

config, fixed_collocation, word_replacement = read_config()

VOICE = config["voice"]
path = config["save_path"]
rate = config["rate"]
pitch = config["pitch"]

def search_fixed_collocation(TEXT):
    for collocation in fixed_collocation:
        if collocation["before"] == TEXT:
            return collocation["after"]
    return TEXT

def re_search_word_replacement(TEXT, replacement):
    return re.sub(replacement["before"], replacement["after"], TEXT)

def search_word_replacement(TEXT: str):
    for replacement in word_replacement:
        if replacement["re"] == True:
            TEXT = re_search_word_replacement(TEXT, replacement)
        else:
            TEXT = TEXT.replace(replacement["before"], replacement["after"])
    return TEXT

def set_topmost():
    hwnd = win32gui.FindWindow(None, root.title())
    win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)

root = tk.Tk()
root.geometry("400x200")
root.title("tts")
# root.after(100, set_topmost)
lock = threading.Lock()

def get_savepath(TEXT):
    encoded_text = TEXT.encode('utf-8')
    hash_object = hashlib.sha256(encoded_text)
    hash_value = hash_object.hexdigest()
    return f"{path}/{hash_value}.mp3"

def accelerate_mp3(TEXT):
    audio_path = get_savepath(TEXT)
    y, sr = librosa.load(audio_path, sr=48000)
    # 变速处理，加速1.5倍
    y_change = librosa.effects.time_stretch(y, rate=1.5)
    # 保存
    sf.write(audio_path, y_change, sr)

def speak(TEXT):
    try:
        # communicate = edge_tts.Communicate(TEXT, VOICE, rate=rate, pitch=pitch)
        tts = gTTS(text=TEXT, lang="zh-cn")
        tts.save(get_savepath(TEXT))
        accelerate_mp3(TEXT)
        play_mp3(TEXT)
    except Exception as e:
        print(f"An error occurred during TTS: {e}")
        if os.path.exists(get_savepath(TEXT)):
            os.remove(get_savepath(TEXT))

# 创建一个Frame来容纳文本框
frame = tk.Frame(root)
frame.place(relx = 0.5, rely = 0.5, anchor = 'center')
# 创建一个文本输入框
prompt_label = tk.Label(frame, text="在此输入语音")
prompt_label.pack()
text_entry = tk.Entry(frame)
text_entry.pack()

def clear_memory(path):
    for file in os.listdir(path):
        file_path = os.path.join(path, file)
        if os.path.isfile(file_path):
            os.remove(file_path)

def update_config():
    global VOICE
    global path
    global rate
    global pitch
    global config
    global fixed_collocation
    global word_replacement
    config, fixed_collocation, word_replacement = read_config()
    VOICE = config["voice"]
    path = config["save_path"]
    rate = config["rate"]
    pitch = config["pitch"]
    clear_memory(path)
    
# button = tk.Button(frame, text="更新配置文件，该操作会清空缓存", command=update_config)
# button.pack()

def play_mp3(TEXT):
    lock.acquire()
    pygame.mixer.init()
    pygame.mixer.music.load(get_savepath(TEXT))
    pygame.mixer.music.play()
    while pygame.mixer.music.get_busy():
        pygame.time.Clock().tick(10)
    pygame.mixer.quit()
    lock.release()

def tts(TEXT):
    save_path = get_savepath(TEXT)
    if not os.path.exists(save_path):
        threading.Thread(target=speak, args=(TEXT,)).start()
    else:
        threading.Thread(target=play_mp3, args=(TEXT,)).start()
def submit_text():
    if text_entry.get() == '':
        return
    TEXT = search_fixed_collocation(text_entry.get())
    TEXT = search_word_replacement(TEXT)
    threading.Thread(target = tts, args=(TEXT,)).start()
    text_entry.delete(0, tk.END)

text_entry.bind('<Return>', lambda event: submit_text())
root.mainloop()